This is an R Markdown
Notebook. Each section of the code is then explained.
First of all import the libraries needed
#install.packages(c("datavolley", "ovlytics"))
library(datavolley)
library(ggplot2)
library(dplyr)
library(ovlytics)
Import the file you are interested in considering more than one
match, you have to import all the folder
filename <- "F:/&##Backup##_R00 HONDA O-ALLIANZ V.dvw"
#d <- dir("C:/Users/mirko/OneDrive - Politecnico di Milano/Altro/Volley/Conco2324/Parella Torino/Ritorno/", pattern = "dvw$", full.names = TRUE)
teamName = 'HONDA OLIVERO S.BERNARDO CUNEO'
x <- dv_read(filename)
serve_idx <- find_serves(plays(x))
table(plays(x)$team[serve_idx])
ALLIANZ VERO VOLLEY MILANO HONDA OLIVERO S.BERNARDO CUNEO
74 64
Funzioni utili
## find rows where a single player is on court
player_on_court <- function(x, target_player_id, team = NULL) {
if (!is.null(team)) team <- match.arg(team, c("home", "visiting"))
## 'team' is optional here, if NULL then we look at both home and visiting teams
idx <- rep(FALSE, nrow(x))
if (is.null(team) || team == "home") {
idx <- idx | x$home_player_id1 == target_player_id | x$home_player_id2 == target_player_id | x$home_player_id3 == target_player_id |
x$home_player_id4 == target_player_id | x$home_player_id5 == target_player_id | x$home_player_id6 == target_player_id
}
if (is.null(team) || team == "visiting") {
idx <- idx | x$visiting_player_id1 == target_player_id | x$visiting_player_id2 == target_player_id | x$visiting_player_id3 == target_player_id |
x$visiting_player_id4 == target_player_id | x$visiting_player_id5 == target_player_id | x$visiting_player_id6 == target_player_id
}
idx[is.na(idx)] <- FALSE
idx
}
## find rows where any of our target players are on court
any_player_on_court <- function(x, target_player_ids, team = NULL) {
## for each target player, find rows where they are on court
out <- lapply(target_player_ids, function(pid) player_on_court(x, target_player_id = pid, team = team))
## and now find rows where ANY of those players were on court
apply(do.call(cbind, out), 1, any)
}
## find rows where all of our target players are on court
all_players_on_court <- function(x, target_player_ids, team = NULL) {
## for each target player, find rows where they are on court
out <- lapply(target_player_ids, function(pid) player_on_court(x, target_player_id = pid, team = team))
## and now find rows where ALL of those players were on court
apply(do.call(cbind, out), 1, all)
}
#d <- dir("D:/Dati/Documents/GitHub/CuneoWebsite.io/Assets/", pattern = "dvw$", full.names = TRUE)
d <- dir("F:/", pattern = "dvw$", full.names = TRUE)
lx <- list()
## read each file
for (fi in seq_along(d)) lx[[fi]] <- dv_read(d[fi], insert_technical_timeouts = FALSE)
## now extract the play-by-play component from each and bind them together
px <- list()
for (fi in seq_along(lx)) px[[fi]] <- plays(lx[[fi]])
px <- do.call(rbind, px)
Rendimento in Battuta
#, end_zone == 5
library("formattable")
table_data <- px %>%
dplyr::filter(skill == "Serve", team == teamName) %>%
group_by(player_name) %>%
dplyr::summarize(
N_battute = n(),
count_perfette = sum(evaluation_code == "#", na.rm = TRUE),
count_positive = sum(evaluation_code == "+", na.rm = TRUE),
#count_escalamative = sum(evaluation_code == "!", na.rm = TRUE),
#count_negative = sum(evaluation_code == "-", na.rm = TRUE),
count_errori = sum(evaluation_code == "=", na.rm = TRUE),
positività = percent((count_positive + count_perfette)/N_battute, digits = 0),
efficienza = percent((count_positive + count_perfette - count_errori)/N_battute, digits = 0),
)
data_plot <- table_data
table_data
# Calculate cumulative statistics for the team
team_total <- table_data %>%
summarise(
N_battute = sum(N_battute),
count_perfette = sum(count_perfette),
count_positive = sum(count_positive),
count_errori = sum(count_errori),
positività = percent(sum(count_positive + count_perfette) / sum(N_battute), digits = 0),
efficienza = percent(sum(count_positive + count_perfette - count_errori) / sum(N_battute), digits = 0)
) %>%
mutate(player_name = "TOT. Squadra") # Add a player_name for the team total row
# Combine the team total row with the original table data
table_data_with_total <- bind_rows(table_data, team_total)
# Print the table with the team total row
table_data_with_total
library(kableExtra)
# Apply custom CSS styling to the entire table
# Reorder columns to make positività the second column
table_data <- table_data_with_total %>%
#select(-efficienza) %>%
select(1, 7, everything())
# Apply custom CSS styling to the entire table
styled_table <- table_data %>%
kable("html") %>%
kable_styling(full_width = FALSE, htmltable_class = 'styled-table', html_font = '"Be Vietnam Pro", sans-serif') %>%
row_spec(9, bold = TRUE) %>%
column_spec(2, background = ifelse(table_data$efficienza >= percent(0.3), "lightgreen",ifelse(table_data$efficienza > percent(0.25) & table_data$efficienza < percent(0.3), "yellow", "lightcoral")))
#row_spec(which(table_data$efficienza > 0.2 & table_data$efficienza < 0.3), background = "yellow") %>%
#row_spec(which(table_data$efficienza <= 0.2), background = "lightcoral")
# Save the styled table to an HTML file
writeLines(as.character(styled_table), "Battuta_tab.html")
library(plotly)
fig <- plot_ly(data_plot,
x = ~positività*100,
y = ~efficienza*100,
type = 'scatter',
mode = 'markers',
hovertemplate = paste('<i>Player</i>: %{text}',
'<br><b>Positività (%)</b>: %{x})',
'<br><b>Efficienza (%)</b>: %{y}',
'<br><b>Battuta</b>: %{marker.size}<extra></extra>'),
color = ~positività,
marker = list(size = ~N_battute, sizemode = "area", sizeref = 0.005, opacity = 0.5),
text = ~player_name
)
fig <- fig %>% layout(title = 'Qualità Battuta',
xaxis = list(title = 'Positività', showgrid = TRUE),
yaxis = list(title = 'Efficienza', showgrid = TRUE)
)
fig
# Assuming you have your data frame 'data_plot' with columns 'player_name' and 'efficienza'
# Calculate the color based on efficienza values
data_plot$color <- ifelse(data_plot$efficienza < 0.25, "red",
ifelse(data_plot$efficienza > 0.30, "green", "yellow"))
fig <- plot_ly(
x = data_plot$player_name,
y = round(data_plot$efficienza, digits = 2),
marker = list(color = data_plot$color),
name = "Efficienza in Battuta",
type = "bar",
text = round(data_plot$efficienza, digits = 2), textposition = 'auto', marker = list(color = 'rgb(158,202,225)', line = list(color = 'rgb(8,48,107)', width = 1.5)))
fig <- fig %>% layout(title = "Efficienza in Battuta",
xaxis = list(title = ""),
yaxis = list(title = ""))
fig
# Customize other plot settings as needed
library(htmlwidgets)
# Assuming 'fig' is your Plotly figure
saveWidget(fig, "Battuta.html")
Rendimento in Ricezione
Ora analizziamo la ricezione:
#, end_zone == 5
table_data <- px %>%
dplyr::filter(skill == "Reception", team == teamName) %>%
group_by(player_name) %>%
dplyr::summarize(
N_receptions = n(),
count_perfette = sum(evaluation_code == "#", na.rm = TRUE),
count_positive = sum(evaluation_code == "+", na.rm = TRUE),
#count_escalamative = sum(evaluation_code == "!", na.rm = TRUE),
#count_negative = sum(evaluation_code == "-", na.rm = TRUE),
count_errori = sum(evaluation_code == "=", na.rm = TRUE),
positività = percent((count_positive + count_perfette)/N_receptions, digits = 0),
efficienza = percent((count_positive + count_perfette - count_errori)/N_receptions, digits = 0),
)
data_plot <- table_data
table_data
# Compute total statistics for the team
total_stats <- table_data %>%
summarise(
N_receptions = sum(N_receptions),
count_perfette = sum(count_perfette),
count_positive = sum(count_positive),
count_errori = sum(count_errori),
positività = percent(sum(count_positive + count_perfette) / sum(N_receptions)),
efficienza = percent(sum(count_positive + count_perfette - count_errori) / sum(N_receptions))
) %>%
mutate(player_name = "TOT. Squadra") # Add a player_name for the team total row
# Combine the team total row with the original table data
table_data_with_total <- bind_rows(table_data, total_stats)
# Print the table with the team total row
table_data_with_total
table_data <- table_data_with_total %>%
#select(-efficienza) %>%
select(1, 7, everything())
# Apply custom CSS styling to the entire table
styled_table <- table_data %>%
kable("html") %>%
kable_styling(full_width = FALSE, htmltable_class = 'styled-table', html_font = '"Be Vietnam Pro", sans-serif') %>%
row_spec(7, bold = TRUE) %>%
column_spec(2,
background = case_when(
(table_data$player_name == "Serena Scognamillo" | table_data$player_name == "Federica Ferrario") ~ ifelse(table_data$efficienza >= percent(0.56), "lightgreen", ifelse(table_data$efficienza > percent(0.48) & table_data$efficienza < percent(0.56), "yellow", "lightcoral")),
(table_data$player_name == "Lena Stigrot" | table_data$player_name == "Anna Haak") ~ ifelse(table_data$efficienza >= percent(0.37), "lightgreen", ifelse(table_data$efficienza > percent(0.31) & table_data$efficienza < percent(0.37), "yellow", "lightcoral")),
(table_data$player_name == "Alice Tanase" | table_data$player_name == "Madison Kubik") ~ ifelse(table_data$efficienza >= percent(0.43), "lightgreen", ifelse(table_data$efficienza > percent(0.37) & table_data$efficienza < percent(0.43), "yellow", "lightcoral")),
TRUE ~ ifelse(table_data$efficienza >= percent(0.42), "lightgreen", ifelse(table_data$efficienza > percent(0.41) & table_data$efficienza < percent(0.42), "orange", "red"))
)
)
Errore in xml_children(x)[[search]] : subscript fuori limite
fig <- plot_ly(data_plot,
x = ~positività*100,
y = ~efficienza*100,
type = 'scatter',
mode = 'markers',
hovertemplate = paste('<i>Player</i>: %{text}',
'<br><b>Positività(%)</b>: %{x}',
'<br><b>Efficienza(%)</b>: %{y}',
'<br><b>Ricezione</b>: %{marker.size}<extra></extra>'),
color = ~positività,
marker = list(size = ~N_receptions, sizemode = "area", sizeref = 0.005, opacity = 0.5),
text = ~player_name
)
fig <- fig %>% layout(title = 'Qualità Ricezione',
xaxis = list(title = 'Positività', showgrid = TRUE),
yaxis = list(title = 'Efficienza', showgrid = TRUE)
)
fig
# Assuming you have your data frame 'data_plot' with columns 'player_name' and 'efficienza'
# Calculate the color based on efficienza values
data_plot <- table_data %>%
mutate(color = case_when(
(player_name %in% c("Serena Scognamillo", "Federica Ferrario") & efficienza >= percent(0.56)) |
(player_name %in% c("Lena Stigrot", "Anna Haak") & efficienza >= percent(0.37)) |
(player_name %in% c("Alice Tanase", "Madison Kubik") & efficienza >= percent(0.43)) |
efficienza >= percent(0.42) ~ "lightgreen",
(player_name %in% c("Serena Scognamillo", "Federica Ferrario") & efficienza > percent(0.48) & efficienza < percent(0.56)) |
(player_name %in% c("Lena Stigrot", "Anna Haak") & efficienza > percent(0.31) & efficienza < percent(0.37)) |
(player_name %in% c("Alice Tanase", "Madison Kubik") & efficienza > percent(0.37) & efficienza < percent(0.43)) |
(efficienza > percent(0.41) & efficienza < percent(0.42)) ~ "orange",
(player_name %in% c("Serena Scognamillo", "Federica Ferrario") & efficienza <= percent(0.48) ) |
(player_name %in% c("Lena Stigrot", "Anna Haak") & efficienza <= percent(0.31)) |
(player_name %in% c("Alice Tanase", "Madison Kubik") & efficienza <= percent(0.37)) |
efficienza < percent(0.41) ~ "lightcoral",
TRUE ~ NA_character_
))
fig <- plot_ly(
x = data_plot$player_name,
y = round(data_plot$efficienza, digits = 2),
marker = list(color = data_plot$color),
name = "Efficienza in Ricezione",
type = "bar",
text = round(data_plot$efficienza, digits = 2), textposition = 'auto', marker = list(color = 'rgb(158,202,225)', line = list(color = 'rgb(8,48,107)', width = 1.5)))
fig <- fig %>% layout(title = "Efficienza in Ricezione",
xaxis = list(title = ""),
yaxis = list(title = ""))
fig
saveWidget(fig, "Ricezione.html")
Rendimento in Attacco
# end_zone == 5
table_data <- px %>%
dplyr::filter(skill == "Attack", team == teamName) %>%
group_by(player_name) %>%
dplyr::summarize(
N_attacks = n(),
count_perfette = sum(evaluation_code == "#", na.rm = TRUE),
count_positive = sum(evaluation_code == "+", na.rm = TRUE),
#count_escalamative = sum(evaluation_code == "!", na.rm = TRUE),
#count_negative = sum(evaluation_code == "-", na.rm = TRUE),
count_errori = sum(evaluation_code == "=", na.rm = TRUE),
positività = percent((count_positive + count_perfette)/N_attacks, digits = 0),
efficienza = percent((count_positive + count_perfette - count_errori)/N_attacks, digits = 0),
)
data_plot <- table_data
table_data
# Compute the total statistics
total_stats <- table_data %>%
summarise(
N_attacks = sum(N_attacks),
count_perfette = sum(count_perfette),
count_positive = sum(count_positive),
count_errori = sum(count_errori),
positività = percent(sum(count_positive + count_perfette) / sum(N_attacks)),
efficienza = percent(sum(count_positive + count_perfette - count_errori) / sum(N_attacks))
) %>%
mutate(player_name = "TOT. Squadra") # Add a player_name for the team total row
# Add the total row to the table data
table_data <- bind_rows(table_data, total_stats)
table_data <- table_data %>%
#select(-efficienza) %>%
select(1, 7, everything())
# Apply custom CSS styling to the entire table
styled_table <- table_data %>%
kable("html") %>%
kable_styling(full_width = FALSE, htmltable_class = 'styled-table', html_font = '"Be Vietnam Pro", sans-serif') %>%
column_spec(2, background = case_when(
(table_data$player_name == "Anna Adelusi" | table_data$player_name == "Terry Ruth Enweonwu") ~ ifelse(table_data$efficienza >= percent(0.27), "lightgreen", ifelse(table_data$efficienza > percent(0.24) & table_data$efficienza < percent(0.27), "yellow", "lightcoral")),
(table_data$player_name == "Anna Haak" | table_data$player_name == "Lena Stigrot") ~ ifelse(table_data$efficienza >= percent(0.34), "lightgreen", ifelse(table_data$efficienza > percent(0.30) & table_data$efficienza < percent(0.34), "yellow", "lightcoral")),
(table_data$player_name == "Alice Tanase" | table_data$player_name == "Madison Kubik") ~ ifelse(table_data$efficienza >= percent(0.24), "lightgreen", ifelse(table_data$efficienza > percent(0.20) & table_data$efficienza < percent(0.24), "yellow", "lightcoral")),
(table_data$player_name == "Saly Thior" | table_data$player_name == "Amandha Sylves" | table_data$player_name == "Anna Hall" | table_data$player_name == "Beatrice Molinaro") ~ ifelse(table_data$efficienza >= percent(0.44), "lightgreen", ifelse(table_data$efficienza > percent(0.38) & table_data$efficienza < percent(0.44), "yellow", "lightcoral")),
TRUE ~ ifelse(table_data$efficienza >= percent(0.42), "lightgreen", ifelse(table_data$efficienza > percent(0.41) & table_data$efficienza < percent(0.42), "orange", "red"))
)
)
# Save the styled table to an HTML file
writeLines(as.character(styled_table), "Attacco_tab.html")
fig <- plot_ly(data_plot,
x = ~positività,
y = ~efficienza,
type = 'scatter',
mode = 'markers',
hovertemplate = paste('<i>Player</i>: %{text}',
'<br><b>Positività(%)</b>: %{x}',
'<br><b>Efficienza(%)</b>: %{y}',
'<br><b>Attacchi</b>: %{marker.size}<extra></extra>'),
color = ~positività,
marker = list(size = ~N_attacks, sizemode = "area", sizeref = 0.005, opacity = 0.5),
text = ~player_name
)
fig <- fig %>% layout(title = 'Qualità Attacco',
xaxis = list(title = 'Positività', showgrid = FALSE),
yaxis = list(title = 'Efficienza', showgrid = FALSE)
)
fig
# Add a new column 'color' based on the rules provided
data_plot <- table_data %>%
mutate(color = case_when(
(player_name %in% c("Anna Adelusi", "Terry Ruth Enweonwu") & efficienza >= percent(0.27)) |
(player_name %in% c("Anna Haak", "Lena Stigrot") & efficienza >= percent(0.34)) |
(player_name %in% c("Alice Tanase", "Madison Kubik") & efficienza >= percent(0.24)) |
(player_name %in% c("Saly Thior", "Amandha Sylves", "Anna Hall", "Beatrice Molinaro") & efficienza >= percent(0.44)) |
efficienza >= percent(0.42) ~ "lightgreen",
(player_name %in% c("Anna Adelusi", "Terry Ruth Enweonwu") & efficienza > percent(0.24) & efficienza < percent(0.27)) |
(player_name %in% c("Anna Haak", "Lena Stigrot") & efficienza > percent(0.30) & efficienza < percent(0.34)) |
(player_name %in% c("Alice Tanase", "Madison Kubik") & efficienza > percent(0.20) & efficienza < percent(0.24)) |
(player_name %in% c("Saly Thior", "Amandha Sylves", "Anna Hall", "Beatrice Molinaro") & efficienza > percent(0.38) & efficienza < percent(0.44)) |
(efficienza > percent(0.41) & efficienza < percent(0.42)) ~ "orange",
(player_name %in% c("Anna Adelusi", "Terry Ruth Enweonwu") & efficienza <= percent(0.24)) |
(player_name %in% c("Anna Haak", "Lena Stigrot") & efficienza <= percent(0.30)) |
(player_name %in% c("Alice Tanase", "Madison Kubik") & efficienza <= percent(0.20)) |
(player_name %in% c("Saly Thior", "Amandha Sylves", "Anna Hall", "Beatrice Molinaro") & efficienza <= percent(0.38)) |
efficienza < percent(0.41) ~ "lightcoral",
TRUE ~ NA_character_
))
fig <- plot_ly(
x = data_plot$player_name,
y = round(data_plot$efficienza, digits = 2),
marker = list(color = data_plot$color),
name = "Efficienza in Attacco",
type = "bar",
text = round(data_plot$efficienza, digits = 2), textposition = 'auto', marker = list(color = 'rgb(158,202,225)', line = list(color = 'rgb(8,48,107)', width = 1.5)))
fig <- fig %>% layout(title = "Efficienza in Attacco",
xaxis = list(title = ""),
yaxis = list(title = ""))
fig
saveWidget(fig, "Attacco.html")
Volleyball Distribution
library(ggplot2)
library(htmlwidgets)
for (i in 1:6) {
attack_rate <- px %>%
dplyr::filter(skill == "Attack", team == teamName, visiting_setter_position == i) %>%
group_by(start_zone) %>%
dplyr::summarize(n_attacks = n()) %>%
mutate(rate = n_attacks/sum(n_attacks)) %>%
ungroup
attack_rate <- cbind(attack_rate, dv_xy(attack_rate$start_zone, end = "lower"))
tm2i <- attack_rate$team == teams(px)[2]
attack_rate[tm2i, c("x", "y")] <- dv_flip_xy(attack_rate[tm2i, c("x", "y")])
attack_rate$rate_rounded <- round(attack_rate$rate, 2)
fig2 <- ggplot(attack_rate, aes(x, y, fill = rate_rounded)) +
geom_tile() +
geom_text(aes(label = paste0(round(rate_rounded * 100, 2), "%")), color = "black", size = 3) +
ggcourt(labels = teams(px)) +
scale_fill_gradient2(name = "Attack rate (%)", labels = scales::percent_format(accuracy = 0.01))
html_name <- paste0("Distribuzione", i, ".png")
# Save the plot as an HTML file
ggsave(html_name, plot = fig2, width = 8, height = 6, units = "in", dpi = 300)
}
LS0tDQp0aXRsZTogIkN1bmVvIERhdGEgQW5hbHlzaXMiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIEVhY2ggc2VjdGlvbiBvZiB0aGUgY29kZSBpcyB0aGVuIGV4cGxhaW5lZC4NCg0KRmlyc3Qgb2YgYWxsIGltcG9ydCB0aGUgbGlicmFyaWVzIG5lZWRlZA0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKGMoImRhdGF2b2xsZXkiLCAib3ZseXRpY3MiKSkNCmxpYnJhcnkoZGF0YXZvbGxleSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KG92bHl0aWNzKQ0KYGBgDQoNCkltcG9ydCB0aGUgZmlsZSB5b3UgYXJlIGludGVyZXN0ZWQgaW4gY29uc2lkZXJpbmcgbW9yZSB0aGFuIG9uZSBtYXRjaCwgeW91IGhhdmUgdG8gaW1wb3J0IGFsbCB0aGUgZm9sZGVyDQoNCmBgYHtyfQ0KZmlsZW5hbWUgPC0gIkY6LyYjI0JhY2t1cCMjX1IwMCBIT05EQSBPLUFMTElBTlogVi5kdnciDQojZCA8LSBkaXIoIkM6L1VzZXJzL21pcmtvL09uZURyaXZlIC0gUG9saXRlY25pY28gZGkgTWlsYW5vL0FsdHJvL1ZvbGxleS9Db25jbzIzMjQvUGFyZWxsYSBUb3Jpbm8vUml0b3Juby8iLCBwYXR0ZXJuID0gImR2dyQiLCBmdWxsLm5hbWVzID0gVFJVRSkNCmBgYA0KDQpgYGB7cn0NCnRlYW1OYW1lID0gJ0hPTkRBIE9MSVZFUk8gUy5CRVJOQVJETyBDVU5FTycNCnggPC0gZHZfcmVhZChmaWxlbmFtZSkNCnNlcnZlX2lkeCA8LSBmaW5kX3NlcnZlcyhwbGF5cyh4KSkNCnRhYmxlKHBsYXlzKHgpJHRlYW1bc2VydmVfaWR4XSkNCmBgYA0KDQpGdW56aW9uaSB1dGlsaQ0KDQpgYGB7cn0NCiMjIGZpbmQgcm93cyB3aGVyZSBhIHNpbmdsZSBwbGF5ZXIgaXMgb24gY291cnQNCnBsYXllcl9vbl9jb3VydCA8LSBmdW5jdGlvbih4LCB0YXJnZXRfcGxheWVyX2lkLCB0ZWFtID0gTlVMTCkgew0KICBpZiAoIWlzLm51bGwodGVhbSkpIHRlYW0gPC0gbWF0Y2guYXJnKHRlYW0sIGMoImhvbWUiLCAidmlzaXRpbmciKSkNCiAgIyMgJ3RlYW0nIGlzIG9wdGlvbmFsIGhlcmUsIGlmIE5VTEwgdGhlbiB3ZSBsb29rIGF0IGJvdGggaG9tZSBhbmQgdmlzaXRpbmcgdGVhbXMNCiAgaWR4IDwtIHJlcChGQUxTRSwgbnJvdyh4KSkNCiAgaWYgKGlzLm51bGwodGVhbSkgfHwgdGVhbSA9PSAiaG9tZSIpIHsNCiAgICBpZHggPC0gaWR4IHwgeCRob21lX3BsYXllcl9pZDEgPT0gdGFyZ2V0X3BsYXllcl9pZCB8IHgkaG9tZV9wbGF5ZXJfaWQyID09IHRhcmdldF9wbGF5ZXJfaWQgfCB4JGhvbWVfcGxheWVyX2lkMyA9PSB0YXJnZXRfcGxheWVyX2lkIHwNCiAgICAgICAgICAgICAgICAgeCRob21lX3BsYXllcl9pZDQgPT0gdGFyZ2V0X3BsYXllcl9pZCB8IHgkaG9tZV9wbGF5ZXJfaWQ1ID09IHRhcmdldF9wbGF5ZXJfaWQgfCB4JGhvbWVfcGxheWVyX2lkNiA9PSB0YXJnZXRfcGxheWVyX2lkDQogIH0NCiAgaWYgKGlzLm51bGwodGVhbSkgfHwgdGVhbSA9PSAidmlzaXRpbmciKSB7DQogICAgaWR4IDwtIGlkeCB8IHgkdmlzaXRpbmdfcGxheWVyX2lkMSA9PSB0YXJnZXRfcGxheWVyX2lkIHwgeCR2aXNpdGluZ19wbGF5ZXJfaWQyID09IHRhcmdldF9wbGF5ZXJfaWQgfCB4JHZpc2l0aW5nX3BsYXllcl9pZDMgPT0gdGFyZ2V0X3BsYXllcl9pZCB8DQogICAgICAgICAgICAgICAgIHgkdmlzaXRpbmdfcGxheWVyX2lkNCA9PSB0YXJnZXRfcGxheWVyX2lkIHwgeCR2aXNpdGluZ19wbGF5ZXJfaWQ1ID09IHRhcmdldF9wbGF5ZXJfaWQgfCB4JHZpc2l0aW5nX3BsYXllcl9pZDYgPT0gdGFyZ2V0X3BsYXllcl9pZA0KICB9DQogIGlkeFtpcy5uYShpZHgpXSA8LSBGQUxTRQ0KICBpZHgNCn0NCg0KIyMgZmluZCByb3dzIHdoZXJlIGFueSBvZiBvdXIgdGFyZ2V0IHBsYXllcnMgYXJlIG9uIGNvdXJ0DQphbnlfcGxheWVyX29uX2NvdXJ0IDwtIGZ1bmN0aW9uKHgsIHRhcmdldF9wbGF5ZXJfaWRzLCB0ZWFtID0gTlVMTCkgew0KICAjIyBmb3IgZWFjaCB0YXJnZXQgcGxheWVyLCBmaW5kIHJvd3Mgd2hlcmUgdGhleSBhcmUgb24gY291cnQNCiAgb3V0IDwtIGxhcHBseSh0YXJnZXRfcGxheWVyX2lkcywgZnVuY3Rpb24ocGlkKSBwbGF5ZXJfb25fY291cnQoeCwgdGFyZ2V0X3BsYXllcl9pZCA9IHBpZCwgdGVhbSA9IHRlYW0pKQ0KICAjIyBhbmQgbm93IGZpbmQgcm93cyB3aGVyZSBBTlkgb2YgdGhvc2UgcGxheWVycyB3ZXJlIG9uIGNvdXJ0DQogIGFwcGx5KGRvLmNhbGwoY2JpbmQsIG91dCksIDEsIGFueSkNCn0NCg0KIyMgZmluZCByb3dzIHdoZXJlIGFsbCBvZiBvdXIgdGFyZ2V0IHBsYXllcnMgYXJlIG9uIGNvdXJ0DQphbGxfcGxheWVyc19vbl9jb3VydCA8LSBmdW5jdGlvbih4LCB0YXJnZXRfcGxheWVyX2lkcywgdGVhbSA9IE5VTEwpIHsNCiAgIyMgZm9yIGVhY2ggdGFyZ2V0IHBsYXllciwgZmluZCByb3dzIHdoZXJlIHRoZXkgYXJlIG9uIGNvdXJ0DQogIG91dCA8LSBsYXBwbHkodGFyZ2V0X3BsYXllcl9pZHMsIGZ1bmN0aW9uKHBpZCkgcGxheWVyX29uX2NvdXJ0KHgsIHRhcmdldF9wbGF5ZXJfaWQgPSBwaWQsIHRlYW0gPSB0ZWFtKSkNCiAgIyMgYW5kIG5vdyBmaW5kIHJvd3Mgd2hlcmUgQUxMIG9mIHRob3NlIHBsYXllcnMgd2VyZSBvbiBjb3VydA0KICBhcHBseShkby5jYWxsKGNiaW5kLCBvdXQpLCAxLCBhbGwpDQp9DQoNCmBgYA0KDQpgYGB7cn0NCiNkIDwtIGRpcigiRDovRGF0aS9Eb2N1bWVudHMvR2l0SHViL0N1bmVvV2Vic2l0ZS5pby9Bc3NldHMvIiwgcGF0dGVybiA9ICJkdnckIiwgZnVsbC5uYW1lcyA9IFRSVUUpDQpkIDwtIGRpcigiRjovIiwgcGF0dGVybiA9ICJkdnckIiwgZnVsbC5uYW1lcyA9IFRSVUUpDQpseCA8LSBsaXN0KCkNCiMjIHJlYWQgZWFjaCBmaWxlDQpmb3IgKGZpIGluIHNlcV9hbG9uZyhkKSkgbHhbW2ZpXV0gPC0gZHZfcmVhZChkW2ZpXSwgaW5zZXJ0X3RlY2huaWNhbF90aW1lb3V0cyA9IEZBTFNFKQ0KIyMgbm93IGV4dHJhY3QgdGhlIHBsYXktYnktcGxheSBjb21wb25lbnQgZnJvbSBlYWNoIGFuZCBiaW5kIHRoZW0gdG9nZXRoZXINCnB4IDwtIGxpc3QoKQ0KZm9yIChmaSBpbiBzZXFfYWxvbmcobHgpKSBweFtbZmldXSA8LSBwbGF5cyhseFtbZmldXSkNCnB4IDwtIGRvLmNhbGwocmJpbmQsIHB4KQ0KDQpgYGANCg0KIyMgUmVuZGltZW50byBpbiBCYXR0dXRhDQoNCmBgYHtyfQ0KIywgZW5kX3pvbmUgPT0gNQ0KbGlicmFyeSgiZm9ybWF0dGFibGUiKSANCnRhYmxlX2RhdGEgPC0gcHggJT4lIA0KICBkcGx5cjo6ZmlsdGVyKHNraWxsID09ICJTZXJ2ZSIsIHRlYW0gPT0gdGVhbU5hbWUpICU+JSANCiAgZ3JvdXBfYnkocGxheWVyX25hbWUpICU+JSANCiAgZHBseXI6OnN1bW1hcml6ZSgNCiAgICBOX2JhdHR1dGUgPSBuKCksDQogICAgY291bnRfcGVyZmV0dGUgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICIjIiwgbmEucm0gPSBUUlVFKSwNCiAgICBjb3VudF9wb3NpdGl2ZSA9IHN1bShldmFsdWF0aW9uX2NvZGUgPT0gIisiLCBuYS5ybSA9IFRSVUUpLA0KICAgICNjb3VudF9lc2NhbGFtYXRpdmUgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICIhIiwgbmEucm0gPSBUUlVFKSwNCiAgICAjY291bnRfbmVnYXRpdmUgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICItIiwgbmEucm0gPSBUUlVFKSwNCiAgICBjb3VudF9lcnJvcmkgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICI9IiwgbmEucm0gPSBUUlVFKSwNCiAgICBwb3NpdGl2aXTDoCA9IHBlcmNlbnQoKGNvdW50X3Bvc2l0aXZlICsgY291bnRfcGVyZmV0dGUpL05fYmF0dHV0ZSwgZGlnaXRzID0gMCksDQogICAgZWZmaWNpZW56YSA9IHBlcmNlbnQoKGNvdW50X3Bvc2l0aXZlICsgY291bnRfcGVyZmV0dGUgLSBjb3VudF9lcnJvcmkpL05fYmF0dHV0ZSwgZGlnaXRzID0gMCksDQogICkNCg0KZGF0YV9wbG90IDwtIHRhYmxlX2RhdGENCg0KdGFibGVfZGF0YQ0KYGBgDQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGUgY3VtdWxhdGl2ZSBzdGF0aXN0aWNzIGZvciB0aGUgdGVhbQ0KdGVhbV90b3RhbCA8LSB0YWJsZV9kYXRhICU+JQ0KICBzdW1tYXJpc2UoDQogICAgTl9iYXR0dXRlID0gc3VtKE5fYmF0dHV0ZSksDQogICAgY291bnRfcGVyZmV0dGUgPSBzdW0oY291bnRfcGVyZmV0dGUpLA0KICAgIGNvdW50X3Bvc2l0aXZlID0gc3VtKGNvdW50X3Bvc2l0aXZlKSwNCiAgICBjb3VudF9lcnJvcmkgPSBzdW0oY291bnRfZXJyb3JpKSwNCiAgICBwb3NpdGl2aXTDoCA9IHBlcmNlbnQoc3VtKGNvdW50X3Bvc2l0aXZlICsgY291bnRfcGVyZmV0dGUpIC8gc3VtKE5fYmF0dHV0ZSksIGRpZ2l0cyA9IDApLA0KICAgIGVmZmljaWVuemEgPSBwZXJjZW50KHN1bShjb3VudF9wb3NpdGl2ZSArIGNvdW50X3BlcmZldHRlIC0gY291bnRfZXJyb3JpKSAvIHN1bShOX2JhdHR1dGUpLCBkaWdpdHMgPSAwKQ0KICApICU+JQ0KICBtdXRhdGUocGxheWVyX25hbWUgPSAiVE9ULiBTcXVhZHJhIikgICMgQWRkIGEgcGxheWVyX25hbWUgZm9yIHRoZSB0ZWFtIHRvdGFsIHJvdw0KDQojIENvbWJpbmUgdGhlIHRlYW0gdG90YWwgcm93IHdpdGggdGhlIG9yaWdpbmFsIHRhYmxlIGRhdGENCnRhYmxlX2RhdGFfd2l0aF90b3RhbCA8LSBiaW5kX3Jvd3ModGFibGVfZGF0YSwgdGVhbV90b3RhbCkNCg0KIyBQcmludCB0aGUgdGFibGUgd2l0aCB0aGUgdGVhbSB0b3RhbCByb3cNCnRhYmxlX2RhdGFfd2l0aF90b3RhbA0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KDQojIEFwcGx5IGN1c3RvbSBDU1Mgc3R5bGluZyB0byB0aGUgZW50aXJlIHRhYmxlDQojIFJlb3JkZXIgY29sdW1ucyB0byBtYWtlIHBvc2l0aXZpdMOgIHRoZSBzZWNvbmQgY29sdW1uDQp0YWJsZV9kYXRhIDwtIHRhYmxlX2RhdGFfd2l0aF90b3RhbCAlPiUNCiAgI3NlbGVjdCgtZWZmaWNpZW56YSkgJT4lDQogIHNlbGVjdCgxLCA3LCBldmVyeXRoaW5nKCkpDQoNCiMgQXBwbHkgY3VzdG9tIENTUyBzdHlsaW5nIHRvIHRoZSBlbnRpcmUgdGFibGUNCnN0eWxlZF90YWJsZSA8LSB0YWJsZV9kYXRhICU+JQ0KICBrYWJsZSgiaHRtbCIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgaHRtbHRhYmxlX2NsYXNzID0gJ3N0eWxlZC10YWJsZScsIGh0bWxfZm9udCA9ICciQmUgVmlldG5hbSBQcm8iLCBzYW5zLXNlcmlmJykgJT4lDQogIHJvd19zcGVjKDksIGJvbGQgPSBUUlVFKSAlPiUNCiAgY29sdW1uX3NwZWMoMiwgYmFja2dyb3VuZCA9IGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjMpLCAibGlnaHRncmVlbiIsaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+IHBlcmNlbnQoMC4yNSkgJiB0YWJsZV9kYXRhJGVmZmljaWVuemEgPCBwZXJjZW50KDAuMyksICJ5ZWxsb3ciLCAibGlnaHRjb3JhbCIpKSkNCg0KICAjcm93X3NwZWMod2hpY2godGFibGVfZGF0YSRlZmZpY2llbnphID4gMC4yICYgdGFibGVfZGF0YSRlZmZpY2llbnphIDwgMC4zKSwgYmFja2dyb3VuZCA9ICJ5ZWxsb3ciKSAlPiUNCiAgI3Jvd19zcGVjKHdoaWNoKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA8PSAwLjIpLCBiYWNrZ3JvdW5kID0gImxpZ2h0Y29yYWwiKQ0KDQojIFNhdmUgdGhlIHN0eWxlZCB0YWJsZSB0byBhbiBIVE1MIGZpbGUNCndyaXRlTGluZXMoYXMuY2hhcmFjdGVyKHN0eWxlZF90YWJsZSksICJCYXR0dXRhX3RhYi5odG1sIikNCg0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShwbG90bHkpDQoNCmZpZyA8LSBwbG90X2x5KGRhdGFfcGxvdCwgDQogICAgICAgICAgICAgICB4ID0gfnBvc2l0aXZpdMOgKjEwMCwgDQogICAgICAgICAgICAgICB5ID0gfmVmZmljaWVuemEqMTAwLA0KICAgICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgDQogICAgICAgICAgICAgICBtb2RlID0gJ21hcmtlcnMnLCANCiAgICAgICAgICAgICAgIGhvdmVydGVtcGxhdGUgPSBwYXN0ZSgnPGk+UGxheWVyPC9pPjogJXt0ZXh0fScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzxicj48Yj5Qb3NpdGl2aXTDoCAoJSk8L2I+OiAle3h9KScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzxicj48Yj5FZmZpY2llbnphICglKTwvYj46ICV7eX0nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+PGI+QmF0dHV0YTwvYj46ICV7bWFya2VyLnNpemV9PGV4dHJhPjwvZXh0cmE+JyksDQogICAgICAgICAgICAgICBjb2xvciA9IH5wb3NpdGl2aXTDoCwNCiAgICAgICAgICAgICAgIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IH5OX2JhdHR1dGUsIHNpemVtb2RlID0gImFyZWEiLCBzaXplcmVmID0gMC4wMDUsIG9wYWNpdHkgPSAwLjUpLA0KICAgICAgICAgICAgICAgdGV4dCA9IH5wbGF5ZXJfbmFtZQ0KKQ0KDQpmaWcgPC0gZmlnICU+JSBsYXlvdXQodGl0bGUgPSAnUXVhbGl0w6AgQmF0dHV0YScsDQogICAgICAgICAgICAgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gJ1Bvc2l0aXZpdMOgJywgc2hvd2dyaWQgPSBUUlVFKSwNCiAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAnRWZmaWNpZW56YScsIHNob3dncmlkID0gVFJVRSkNCikNCg0KZmlnDQpgYGANCg0KYGBge3J9DQojIEFzc3VtaW5nIHlvdSBoYXZlIHlvdXIgZGF0YSBmcmFtZSAnZGF0YV9wbG90JyB3aXRoIGNvbHVtbnMgJ3BsYXllcl9uYW1lJyBhbmQgJ2VmZmljaWVuemEnDQoNCiMgQ2FsY3VsYXRlIHRoZSBjb2xvciBiYXNlZCBvbiBlZmZpY2llbnphIHZhbHVlcw0KZGF0YV9wbG90JGNvbG9yIDwtIGlmZWxzZShkYXRhX3Bsb3QkZWZmaWNpZW56YSA8IDAuMjUsICJyZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZGF0YV9wbG90JGVmZmljaWVuemEgPiAwLjMwLCAiZ3JlZW4iLCAieWVsbG93IikpDQoNCg0KZmlnIDwtIHBsb3RfbHkoDQogIHggPSBkYXRhX3Bsb3QkcGxheWVyX25hbWUsDQogIHkgPSByb3VuZChkYXRhX3Bsb3QkZWZmaWNpZW56YSwgZGlnaXRzID0gMiksDQogIG1hcmtlciA9IGxpc3QoY29sb3IgPSBkYXRhX3Bsb3QkY29sb3IpLA0KICBuYW1lID0gIkVmZmljaWVuemEgaW4gQmF0dHV0YSIsDQogIHR5cGUgPSAiYmFyIiwNCiAgdGV4dCA9IHJvdW5kKGRhdGFfcGxvdCRlZmZpY2llbnphLCBkaWdpdHMgPSAyKSwgdGV4dHBvc2l0aW9uID0gJ2F1dG8nLCBtYXJrZXIgPSBsaXN0KGNvbG9yID0gJ3JnYigxNTgsMjAyLDIyNSknLCBsaW5lID0gbGlzdChjb2xvciA9ICdyZ2IoOCw0OCwxMDcpJywgd2lkdGggPSAxLjUpKSkNCg0KZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gIkVmZmljaWVuemEgaW4gQmF0dHV0YSIsDQoNCiAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICIiKSwNCg0KICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIiIpKQ0KDQoNCmZpZw0KDQojIEN1c3RvbWl6ZSBvdGhlciBwbG90IHNldHRpbmdzIGFzIG5lZWRlZA0KDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KGh0bWx3aWRnZXRzKQ0KDQojIEFzc3VtaW5nICdmaWcnIGlzIHlvdXIgUGxvdGx5IGZpZ3VyZQ0Kc2F2ZVdpZGdldChmaWcsICJCYXR0dXRhLmh0bWwiKQ0KYGBgDQoNCiMjIFJlbmRpbWVudG8gaW4gUmljZXppb25lDQoNCk9yYSBhbmFsaXp6aWFtbyBsYSByaWNlemlvbmU6DQoNCmBgYHtyfQ0KIywgZW5kX3pvbmUgPT0gNQ0KdGFibGVfZGF0YSA8LSBweCAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoc2tpbGwgPT0gIlJlY2VwdGlvbiIsIHRlYW0gPT0gdGVhbU5hbWUpICU+JSANCiAgZ3JvdXBfYnkocGxheWVyX25hbWUpICU+JSANCiAgZHBseXI6OnN1bW1hcml6ZSgNCiAgICBOX3JlY2VwdGlvbnMgPSBuKCksDQogICAgY291bnRfcGVyZmV0dGUgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICIjIiwgbmEucm0gPSBUUlVFKSwNCiAgICBjb3VudF9wb3NpdGl2ZSA9IHN1bShldmFsdWF0aW9uX2NvZGUgPT0gIisiLCBuYS5ybSA9IFRSVUUpLA0KICAgICNjb3VudF9lc2NhbGFtYXRpdmUgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICIhIiwgbmEucm0gPSBUUlVFKSwNCiAgICAjY291bnRfbmVnYXRpdmUgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICItIiwgbmEucm0gPSBUUlVFKSwNCiAgICBjb3VudF9lcnJvcmkgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICI9IiwgbmEucm0gPSBUUlVFKSwNCiAgICBwb3NpdGl2aXTDoCA9IHBlcmNlbnQoKGNvdW50X3Bvc2l0aXZlICsgY291bnRfcGVyZmV0dGUpL05fcmVjZXB0aW9ucywgZGlnaXRzID0gMCksDQogICAgZWZmaWNpZW56YSA9IHBlcmNlbnQoKGNvdW50X3Bvc2l0aXZlICsgY291bnRfcGVyZmV0dGUgLSBjb3VudF9lcnJvcmkpL05fcmVjZXB0aW9ucywgZGlnaXRzID0gMCksDQogICkNCg0KZGF0YV9wbG90IDwtIHRhYmxlX2RhdGENCg0KdGFibGVfZGF0YQ0KYGBgDQoNCmBgYHtyfQ0KIyBDb21wdXRlIHRvdGFsIHN0YXRpc3RpY3MgZm9yIHRoZSB0ZWFtDQp0b3RhbF9zdGF0cyA8LSB0YWJsZV9kYXRhICU+JQ0KICBzdW1tYXJpc2UoDQogICAgTl9yZWNlcHRpb25zID0gc3VtKE5fcmVjZXB0aW9ucyksDQogICAgY291bnRfcGVyZmV0dGUgPSBzdW0oY291bnRfcGVyZmV0dGUpLA0KICAgIGNvdW50X3Bvc2l0aXZlID0gc3VtKGNvdW50X3Bvc2l0aXZlKSwNCiAgICBjb3VudF9lcnJvcmkgPSBzdW0oY291bnRfZXJyb3JpKSwNCiAgICBwb3NpdGl2aXTDoCA9IHBlcmNlbnQoc3VtKGNvdW50X3Bvc2l0aXZlICsgY291bnRfcGVyZmV0dGUpIC8gc3VtKE5fcmVjZXB0aW9ucykpLA0KICAgIGVmZmljaWVuemEgPSBwZXJjZW50KHN1bShjb3VudF9wb3NpdGl2ZSArIGNvdW50X3BlcmZldHRlIC0gY291bnRfZXJyb3JpKSAvIHN1bShOX3JlY2VwdGlvbnMpKQ0KICApICU+JQ0KICBtdXRhdGUocGxheWVyX25hbWUgPSAiVE9ULiBTcXVhZHJhIikgICMgQWRkIGEgcGxheWVyX25hbWUgZm9yIHRoZSB0ZWFtIHRvdGFsIHJvdw0KDQojIENvbWJpbmUgdGhlIHRlYW0gdG90YWwgcm93IHdpdGggdGhlIG9yaWdpbmFsIHRhYmxlIGRhdGENCnRhYmxlX2RhdGFfd2l0aF90b3RhbCA8LSBiaW5kX3Jvd3ModGFibGVfZGF0YSwgdG90YWxfc3RhdHMpDQoNCiMgUHJpbnQgdGhlIHRhYmxlIHdpdGggdGhlIHRlYW0gdG90YWwgcm93DQp0YWJsZV9kYXRhX3dpdGhfdG90YWwNCmBgYA0KDQpgYGB7cn0NCnRhYmxlX2RhdGEgPC0gdGFibGVfZGF0YV93aXRoX3RvdGFsICU+JQ0KICAjc2VsZWN0KC1lZmZpY2llbnphKSAlPiUNCiAgc2VsZWN0KDEsIDcsIGV2ZXJ5dGhpbmcoKSkNCg0KIyBBcHBseSBjdXN0b20gQ1NTIHN0eWxpbmcgdG8gdGhlIGVudGlyZSB0YWJsZQ0Kc3R5bGVkX3RhYmxlIDwtIHRhYmxlX2RhdGEgJT4lDQogIGthYmxlKCJodG1sIikgJT4lDQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFLCBodG1sdGFibGVfY2xhc3MgPSAnc3R5bGVkLXRhYmxlJywgaHRtbF9mb250ID0gJyJCZSBWaWV0bmFtIFBybyIsIHNhbnMtc2VyaWYnKSAlPiUNCiAgcm93X3NwZWMoNywgYm9sZCA9IFRSVUUpICU+JQ0KICBjb2x1bW5fc3BlYygyLCANCiAgICAgICAgICAgICAgYmFja2dyb3VuZCA9IGNhc2Vfd2hlbigNCiAgICAgICAgICAgICAgICAodGFibGVfZGF0YSRwbGF5ZXJfbmFtZSA9PSAiU2VyZW5hIFNjb2duYW1pbGxvIiB8IHRhYmxlX2RhdGEkcGxheWVyX25hbWUgPT0gIkZlZGVyaWNhIEZlcnJhcmlvIikgfiBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID49IHBlcmNlbnQoMC41NiksICJsaWdodGdyZWVuIiwgaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+IHBlcmNlbnQoMC40OCkgJiB0YWJsZV9kYXRhJGVmZmljaWVuemEgPCBwZXJjZW50KDAuNTYpLCAieWVsbG93IiwgImxpZ2h0Y29yYWwiKSksDQogICAgICAgICAgICAgICAgKHRhYmxlX2RhdGEkcGxheWVyX25hbWUgPT0gIkxlbmEgU3RpZ3JvdCIgfCB0YWJsZV9kYXRhJHBsYXllcl9uYW1lID09ICJBbm5hIEhhYWsiKSB+IGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjM3KSwgImxpZ2h0Z3JlZW4iLCBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID4gcGVyY2VudCgwLjMxKSAmIHRhYmxlX2RhdGEkZWZmaWNpZW56YSA8IHBlcmNlbnQoMC4zNyksICJ5ZWxsb3ciLCAibGlnaHRjb3JhbCIpKSwNCiAgICAgICAgICAgICAgICAodGFibGVfZGF0YSRwbGF5ZXJfbmFtZSA9PSAiQWxpY2UgVGFuYXNlIiB8IHRhYmxlX2RhdGEkcGxheWVyX25hbWUgPT0gIk1hZGlzb24gS3ViaWsiKSB+IGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjQzKSwgImxpZ2h0Z3JlZW4iLCBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID4gcGVyY2VudCgwLjM3KSAmIHRhYmxlX2RhdGEkZWZmaWNpZW56YSA8IHBlcmNlbnQoMC40MyksICJ5ZWxsb3ciLCAibGlnaHRjb3JhbCIpKSwNCiAgICAgICAgICAgICAgICBUUlVFIH4gaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+PSBwZXJjZW50KDAuNDIpLCAibGlnaHRncmVlbiIsIGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPiBwZXJjZW50KDAuNDEpICYgdGFibGVfZGF0YSRlZmZpY2llbnphIDwgcGVyY2VudCgwLjQyKSwgIm9yYW5nZSIsICJyZWQiKSkNCiAgICAgICAgICAgICAgKQ0KICAgICAgICAgICAgICApDQoNCiMgU2F2ZSB0aGUgc3R5bGVkIHRhYmxlIHRvIGFuIEhUTUwgZmlsZQ0Kd3JpdGVMaW5lcyhhcy5jaGFyYWN0ZXIoc3R5bGVkX3RhYmxlKSwgIlJpY2V6aW9uZV90YWIuaHRtbCIpDQpgYGANCg0KYGBge3J9DQpmaWcgPC0gcGxvdF9seShkYXRhX3Bsb3QsIA0KICAgICAgICAgICAgICAgeCA9IH5wb3NpdGl2aXTDoCoxMDAsIA0KICAgICAgICAgICAgICAgeSA9IH5lZmZpY2llbnphKjEwMCwNCiAgICAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIA0KICAgICAgICAgICAgICAgbW9kZSA9ICdtYXJrZXJzJywgDQogICAgICAgICAgICAgICBob3ZlcnRlbXBsYXRlID0gcGFzdGUoJzxpPlBsYXllcjwvaT46ICV7dGV4dH0nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+PGI+UG9zaXRpdml0w6AoJSk8L2I+OiAle3h9JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPjxiPkVmZmljaWVuemEoJSk8L2I+OiAle3l9JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPjxiPlJpY2V6aW9uZTwvYj46ICV7bWFya2VyLnNpemV9PGV4dHJhPjwvZXh0cmE+JyksDQogICAgICAgICAgICAgICBjb2xvciA9IH5wb3NpdGl2aXTDoCwNCiAgICAgICAgICAgICAgIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IH5OX3JlY2VwdGlvbnMsIHNpemVtb2RlID0gImFyZWEiLCBzaXplcmVmID0gMC4wMDUsIG9wYWNpdHkgPSAwLjUpLA0KICAgICAgICAgICAgICAgdGV4dCA9IH5wbGF5ZXJfbmFtZQ0KKQ0KDQpmaWcgPC0gZmlnICU+JSBsYXlvdXQodGl0bGUgPSAnUXVhbGl0w6AgUmljZXppb25lJywNCiAgICAgICAgICAgICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAnUG9zaXRpdml0w6AnLCBzaG93Z3JpZCA9IFRSVUUpLA0KICAgICAgICAgICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICdFZmZpY2llbnphJywgc2hvd2dyaWQgPSBUUlVFKQ0KKQ0KZmlnDQpgYGANCg0KYGBge3J9DQojIEFzc3VtaW5nIHlvdSBoYXZlIHlvdXIgZGF0YSBmcmFtZSAnZGF0YV9wbG90JyB3aXRoIGNvbHVtbnMgJ3BsYXllcl9uYW1lJyBhbmQgJ2VmZmljaWVuemEnDQoNCiMgQ2FsY3VsYXRlIHRoZSBjb2xvciBiYXNlZCBvbiBlZmZpY2llbnphIHZhbHVlcw0KZGF0YV9wbG90IDwtIHRhYmxlX2RhdGEgJT4lDQogIG11dGF0ZShjb2xvciA9IGNhc2Vfd2hlbigNCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJTZXJlbmEgU2NvZ25hbWlsbG8iLCAiRmVkZXJpY2EgRmVycmFyaW8iKSAmIGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjU2KSkgfCANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJMZW5hIFN0aWdyb3QiLCAiQW5uYSBIYWFrIikgJiBlZmZpY2llbnphID49IHBlcmNlbnQoMC4zNykpIHwgDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiQWxpY2UgVGFuYXNlIiwgIk1hZGlzb24gS3ViaWsiKSAmIGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjQzKSkgfA0KICAgIGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjQyKSB+ICJsaWdodGdyZWVuIiwNCiAgICANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJTZXJlbmEgU2NvZ25hbWlsbG8iLCAiRmVkZXJpY2EgRmVycmFyaW8iKSAmIGVmZmljaWVuemEgPiBwZXJjZW50KDAuNDgpICYgZWZmaWNpZW56YSA8IHBlcmNlbnQoMC41NikpIHwgDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiTGVuYSBTdGlncm90IiwgIkFubmEgSGFhayIpICYgZWZmaWNpZW56YSA+IHBlcmNlbnQoMC4zMSkgJiBlZmZpY2llbnphIDwgcGVyY2VudCgwLjM3KSkgfCANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJBbGljZSBUYW5hc2UiLCAiTWFkaXNvbiBLdWJpayIpICYgZWZmaWNpZW56YSA+IHBlcmNlbnQoMC4zNykgJiBlZmZpY2llbnphIDwgcGVyY2VudCgwLjQzKSkgfA0KICAgIChlZmZpY2llbnphID4gcGVyY2VudCgwLjQxKSAmIGVmZmljaWVuemEgPCBwZXJjZW50KDAuNDIpKSB+ICJvcmFuZ2UiLA0KICAgIA0KICAgIChwbGF5ZXJfbmFtZSAlaW4lIGMoIlNlcmVuYSBTY29nbmFtaWxsbyIsICJGZWRlcmljYSBGZXJyYXJpbyIpICYgZWZmaWNpZW56YSA8PSBwZXJjZW50KDAuNDgpICkgfCANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJMZW5hIFN0aWdyb3QiLCAiQW5uYSBIYWFrIikgJiBlZmZpY2llbnphIDw9IHBlcmNlbnQoMC4zMSkpIHwgDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiQWxpY2UgVGFuYXNlIiwgIk1hZGlzb24gS3ViaWsiKSAmIGVmZmljaWVuemEgPD0gcGVyY2VudCgwLjM3KSkgfA0KICAgIGVmZmljaWVuemEgPCBwZXJjZW50KDAuNDEpIH4gImxpZ2h0Y29yYWwiLA0KICAgIA0KICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfDQogICkpDQoNCg0KDQpmaWcgPC0gcGxvdF9seSgNCiAgeCA9IGRhdGFfcGxvdCRwbGF5ZXJfbmFtZSwNCiAgeSA9IHJvdW5kKGRhdGFfcGxvdCRlZmZpY2llbnphLCBkaWdpdHMgPSAyKSwNCiAgbWFya2VyID0gbGlzdChjb2xvciA9IGRhdGFfcGxvdCRjb2xvciksDQogIG5hbWUgPSAiRWZmaWNpZW56YSBpbiBSaWNlemlvbmUiLA0KICB0eXBlID0gImJhciIsDQogIHRleHQgPSByb3VuZChkYXRhX3Bsb3QkZWZmaWNpZW56YSwgZGlnaXRzID0gMiksIHRleHRwb3NpdGlvbiA9ICdhdXRvJywgbWFya2VyID0gbGlzdChjb2xvciA9ICdyZ2IoMTU4LDIwMiwyMjUpJywgbGluZSA9IGxpc3QoY29sb3IgPSAncmdiKDgsNDgsMTA3KScsIHdpZHRoID0gMS41KSkpDQoNCmZpZyA8LSBmaWcgJT4lIGxheW91dCh0aXRsZSA9ICJFZmZpY2llbnphIGluIFJpY2V6aW9uZSIsDQoNCiAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICIiKSwNCg0KICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIiIpKQ0KDQoNCmZpZw0KYGBgDQoNCmBgYHtyfQ0Kc2F2ZVdpZGdldChmaWcsICJSaWNlemlvbmUuaHRtbCIpDQpgYGANCg0KIyMgUmVuZGltZW50byBpbiBBdHRhY2NvDQoNCmBgYHtyfQ0KIyAgZW5kX3pvbmUgPT0gNQ0KdGFibGVfZGF0YSA8LSBweCAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoc2tpbGwgPT0gIkF0dGFjayIsIHRlYW0gPT0gdGVhbU5hbWUpICU+JSANCiAgZ3JvdXBfYnkocGxheWVyX25hbWUpICU+JSANCiAgZHBseXI6OnN1bW1hcml6ZSgNCiAgICBOX2F0dGFja3MgPSBuKCksDQogICAgY291bnRfcGVyZmV0dGUgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICIjIiwgbmEucm0gPSBUUlVFKSwNCiAgICBjb3VudF9wb3NpdGl2ZSA9IHN1bShldmFsdWF0aW9uX2NvZGUgPT0gIisiLCBuYS5ybSA9IFRSVUUpLA0KICAgICNjb3VudF9lc2NhbGFtYXRpdmUgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICIhIiwgbmEucm0gPSBUUlVFKSwNCiAgICAjY291bnRfbmVnYXRpdmUgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICItIiwgbmEucm0gPSBUUlVFKSwNCiAgICBjb3VudF9lcnJvcmkgPSBzdW0oZXZhbHVhdGlvbl9jb2RlID09ICI9IiwgbmEucm0gPSBUUlVFKSwNCiAgICBwb3NpdGl2aXTDoCA9IHBlcmNlbnQoKGNvdW50X3Bvc2l0aXZlICsgY291bnRfcGVyZmV0dGUpL05fYXR0YWNrcywgZGlnaXRzID0gMCksDQogICAgZWZmaWNpZW56YSA9IHBlcmNlbnQoKGNvdW50X3Bvc2l0aXZlICsgY291bnRfcGVyZmV0dGUgLSBjb3VudF9lcnJvcmkpL05fYXR0YWNrcywgZGlnaXRzID0gMCksDQogICkNCg0KZGF0YV9wbG90IDwtIHRhYmxlX2RhdGENCg0KdGFibGVfZGF0YQ0KYGBgDQoNCmBgYHtyfQ0KIyBDb21wdXRlIHRoZSB0b3RhbCBzdGF0aXN0aWNzDQp0b3RhbF9zdGF0cyA8LSB0YWJsZV9kYXRhICU+JQ0KICBzdW1tYXJpc2UoDQogICAgTl9hdHRhY2tzID0gc3VtKE5fYXR0YWNrcyksDQogICAgY291bnRfcGVyZmV0dGUgPSBzdW0oY291bnRfcGVyZmV0dGUpLA0KICAgIGNvdW50X3Bvc2l0aXZlID0gc3VtKGNvdW50X3Bvc2l0aXZlKSwNCiAgICBjb3VudF9lcnJvcmkgPSBzdW0oY291bnRfZXJyb3JpKSwNCiAgICBwb3NpdGl2aXTDoCA9IHBlcmNlbnQoc3VtKGNvdW50X3Bvc2l0aXZlICsgY291bnRfcGVyZmV0dGUpIC8gc3VtKE5fYXR0YWNrcykpLA0KICAgIGVmZmljaWVuemEgPSBwZXJjZW50KHN1bShjb3VudF9wb3NpdGl2ZSArIGNvdW50X3BlcmZldHRlIC0gY291bnRfZXJyb3JpKSAvIHN1bShOX2F0dGFja3MpKQ0KICApICU+JQ0KICBtdXRhdGUocGxheWVyX25hbWUgPSAiVE9ULiBTcXVhZHJhIikgICMgQWRkIGEgcGxheWVyX25hbWUgZm9yIHRoZSB0ZWFtIHRvdGFsIHJvdw0KDQojIEFkZCB0aGUgdG90YWwgcm93IHRvIHRoZSB0YWJsZSBkYXRhDQp0YWJsZV9kYXRhIDwtIGJpbmRfcm93cyh0YWJsZV9kYXRhLCB0b3RhbF9zdGF0cykNCmBgYA0KDQpgYGB7cn0NCnRhYmxlX2RhdGEgPC0gdGFibGVfZGF0YSAlPiUNCiAgI3NlbGVjdCgtZWZmaWNpZW56YSkgJT4lDQogIHNlbGVjdCgxLCA3LCBldmVyeXRoaW5nKCkpDQoNCiMgQXBwbHkgY3VzdG9tIENTUyBzdHlsaW5nIHRvIHRoZSBlbnRpcmUgdGFibGUNCnN0eWxlZF90YWJsZSA8LSB0YWJsZV9kYXRhICU+JQ0KICBrYWJsZSgiaHRtbCIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgaHRtbHRhYmxlX2NsYXNzID0gJ3N0eWxlZC10YWJsZScsIGh0bWxfZm9udCA9ICciQmUgVmlldG5hbSBQcm8iLCBzYW5zLXNlcmlmJykgJT4lDQogIGNvbHVtbl9zcGVjKDIsIGJhY2tncm91bmQgPSBjYXNlX3doZW4oDQogICAgICAgICAgICAgICAgKHRhYmxlX2RhdGEkcGxheWVyX25hbWUgPT0gIkFubmEgQWRlbHVzaSIgfCB0YWJsZV9kYXRhJHBsYXllcl9uYW1lID09ICJUZXJyeSBSdXRoIEVud2Vvbnd1IikgfiBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID49IHBlcmNlbnQoMC4yNyksICJsaWdodGdyZWVuIiwgaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+IHBlcmNlbnQoMC4yNCkgJiB0YWJsZV9kYXRhJGVmZmljaWVuemEgPCBwZXJjZW50KDAuMjcpLCAieWVsbG93IiwgImxpZ2h0Y29yYWwiKSksDQogICAgICAgICAgICAgICAgKHRhYmxlX2RhdGEkcGxheWVyX25hbWUgPT0gIkFubmEgSGFhayIgfCB0YWJsZV9kYXRhJHBsYXllcl9uYW1lID09ICJMZW5hIFN0aWdyb3QiKSB+IGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjM0KSwgImxpZ2h0Z3JlZW4iLCBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID4gcGVyY2VudCgwLjMwKSAmIHRhYmxlX2RhdGEkZWZmaWNpZW56YSA8IHBlcmNlbnQoMC4zNCksICJ5ZWxsb3ciLCAibGlnaHRjb3JhbCIpKSwNCiAgICAgICAgICAgICAgICAodGFibGVfZGF0YSRwbGF5ZXJfbmFtZSA9PSAiQWxpY2UgVGFuYXNlIiB8IHRhYmxlX2RhdGEkcGxheWVyX25hbWUgPT0gIk1hZGlzb24gS3ViaWsiKSB+IGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjI0KSwgImxpZ2h0Z3JlZW4iLCBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID4gcGVyY2VudCgwLjIwKSAmIHRhYmxlX2RhdGEkZWZmaWNpZW56YSA8IHBlcmNlbnQoMC4yNCksICJ5ZWxsb3ciLCAibGlnaHRjb3JhbCIpKSwNCiAgICAgICAgICAgICAgICAodGFibGVfZGF0YSRwbGF5ZXJfbmFtZSA9PSAiU2FseSBUaGlvciIgfCB0YWJsZV9kYXRhJHBsYXllcl9uYW1lID09ICJBbWFuZGhhIFN5bHZlcyIgfCB0YWJsZV9kYXRhJHBsYXllcl9uYW1lID09ICJBbm5hIEhhbGwiIHwgdGFibGVfZGF0YSRwbGF5ZXJfbmFtZSA9PSAiQmVhdHJpY2UgTW9saW5hcm8iKSB+IGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjQ0KSwgImxpZ2h0Z3JlZW4iLCBpZmVsc2UodGFibGVfZGF0YSRlZmZpY2llbnphID4gcGVyY2VudCgwLjM4KSAmIHRhYmxlX2RhdGEkZWZmaWNpZW56YSA8IHBlcmNlbnQoMC40NCksICJ5ZWxsb3ciLCAibGlnaHRjb3JhbCIpKSwNCiAgICAgICAgICAgICAgICBUUlVFIH4gaWZlbHNlKHRhYmxlX2RhdGEkZWZmaWNpZW56YSA+PSBwZXJjZW50KDAuNDIpLCAibGlnaHRncmVlbiIsIGlmZWxzZSh0YWJsZV9kYXRhJGVmZmljaWVuemEgPiBwZXJjZW50KDAuNDEpICYgdGFibGVfZGF0YSRlZmZpY2llbnphIDwgcGVyY2VudCgwLjQyKSwgIm9yYW5nZSIsICJyZWQiKSkNCiAgICAgICAgICAgICAgKQ0KICAgICAgICAgICAgICApDQoNCiMgU2F2ZSB0aGUgc3R5bGVkIHRhYmxlIHRvIGFuIEhUTUwgZmlsZQ0Kd3JpdGVMaW5lcyhhcy5jaGFyYWN0ZXIoc3R5bGVkX3RhYmxlKSwgIkF0dGFjY29fdGFiLmh0bWwiKQ0KYGBgDQoNCmBgYHtyfQ0KZmlnIDwtIHBsb3RfbHkoZGF0YV9wbG90LCANCiAgICAgICAgICAgICAgIHggPSB+cG9zaXRpdml0w6AsIA0KICAgICAgICAgICAgICAgeSA9IH5lZmZpY2llbnphLA0KICAgICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgDQogICAgICAgICAgICAgICBtb2RlID0gJ21hcmtlcnMnLCANCiAgICAgICAgICAgICAgIGhvdmVydGVtcGxhdGUgPSBwYXN0ZSgnPGk+UGxheWVyPC9pPjogJXt0ZXh0fScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzxicj48Yj5Qb3NpdGl2aXTDoCglKTwvYj46ICV7eH0nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+PGI+RWZmaWNpZW56YSglKTwvYj46ICV7eX0nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+PGI+QXR0YWNjaGk8L2I+OiAle21hcmtlci5zaXplfTxleHRyYT48L2V4dHJhPicpLA0KICAgICAgICAgICAgICAgY29sb3IgPSB+cG9zaXRpdml0w6AsDQogICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSB+Tl9hdHRhY2tzLCBzaXplbW9kZSA9ICJhcmVhIiwgc2l6ZXJlZiA9IDAuMDA1LCBvcGFjaXR5ID0gMC41KSwNCiAgICAgICAgICAgICAgIHRleHQgPSB+cGxheWVyX25hbWUNCikNCg0KZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gJ1F1YWxpdMOgIEF0dGFjY28nLA0KICAgICAgICAgICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICdQb3NpdGl2aXTDoCcsIHNob3dncmlkID0gRkFMU0UpLA0KICAgICAgICAgICAgICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICdFZmZpY2llbnphJywgc2hvd2dyaWQgPSBGQUxTRSkNCikNCg0KZmlnDQpgYGANCg0KYGBge3J9DQojIEFkZCBhIG5ldyBjb2x1bW4gJ2NvbG9yJyBiYXNlZCBvbiB0aGUgcnVsZXMgcHJvdmlkZWQNCmRhdGFfcGxvdCA8LSB0YWJsZV9kYXRhICU+JQ0KICBtdXRhdGUoY29sb3IgPSBjYXNlX3doZW4oDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiQW5uYSBBZGVsdXNpIiwgIlRlcnJ5IFJ1dGggRW53ZW9ud3UiKSAmIGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjI3KSkgfCANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJBbm5hIEhhYWsiLCAiTGVuYSBTdGlncm90IikgJiBlZmZpY2llbnphID49IHBlcmNlbnQoMC4zNCkpIHwgDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiQWxpY2UgVGFuYXNlIiwgIk1hZGlzb24gS3ViaWsiKSAmIGVmZmljaWVuemEgPj0gcGVyY2VudCgwLjI0KSkgfA0KICAgIChwbGF5ZXJfbmFtZSAlaW4lIGMoIlNhbHkgVGhpb3IiLCAiQW1hbmRoYSBTeWx2ZXMiLCAiQW5uYSBIYWxsIiwgIkJlYXRyaWNlIE1vbGluYXJvIikgJiBlZmZpY2llbnphID49IHBlcmNlbnQoMC40NCkpIHwNCiAgICBlZmZpY2llbnphID49IHBlcmNlbnQoMC40MikgfiAibGlnaHRncmVlbiIsDQogICAgDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiQW5uYSBBZGVsdXNpIiwgIlRlcnJ5IFJ1dGggRW53ZW9ud3UiKSAmIGVmZmljaWVuemEgPiBwZXJjZW50KDAuMjQpICYgZWZmaWNpZW56YSA8IHBlcmNlbnQoMC4yNykpIHwgDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiQW5uYSBIYWFrIiwgIkxlbmEgU3RpZ3JvdCIpICYgZWZmaWNpZW56YSA+IHBlcmNlbnQoMC4zMCkgJiBlZmZpY2llbnphIDwgcGVyY2VudCgwLjM0KSkgfCANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJBbGljZSBUYW5hc2UiLCAiTWFkaXNvbiBLdWJpayIpICYgZWZmaWNpZW56YSA+IHBlcmNlbnQoMC4yMCkgJiBlZmZpY2llbnphIDwgcGVyY2VudCgwLjI0KSkgfA0KICAgIChwbGF5ZXJfbmFtZSAlaW4lIGMoIlNhbHkgVGhpb3IiLCAiQW1hbmRoYSBTeWx2ZXMiLCAiQW5uYSBIYWxsIiwgIkJlYXRyaWNlIE1vbGluYXJvIikgJiBlZmZpY2llbnphID4gcGVyY2VudCgwLjM4KSAmIGVmZmljaWVuemEgPCBwZXJjZW50KDAuNDQpKSB8DQogICAgKGVmZmljaWVuemEgPiBwZXJjZW50KDAuNDEpICYgZWZmaWNpZW56YSA8IHBlcmNlbnQoMC40MikpIH4gIm9yYW5nZSIsDQogICAgDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiQW5uYSBBZGVsdXNpIiwgIlRlcnJ5IFJ1dGggRW53ZW9ud3UiKSAmIGVmZmljaWVuemEgPD0gcGVyY2VudCgwLjI0KSkgfCANCiAgICAocGxheWVyX25hbWUgJWluJSBjKCJBbm5hIEhhYWsiLCAiTGVuYSBTdGlncm90IikgJiBlZmZpY2llbnphIDw9IHBlcmNlbnQoMC4zMCkpIHwgDQogICAgKHBsYXllcl9uYW1lICVpbiUgYygiQWxpY2UgVGFuYXNlIiwgIk1hZGlzb24gS3ViaWsiKSAmIGVmZmljaWVuemEgPD0gcGVyY2VudCgwLjIwKSkgfA0KICAgIChwbGF5ZXJfbmFtZSAlaW4lIGMoIlNhbHkgVGhpb3IiLCAiQW1hbmRoYSBTeWx2ZXMiLCAiQW5uYSBIYWxsIiwgIkJlYXRyaWNlIE1vbGluYXJvIikgJiBlZmZpY2llbnphIDw9IHBlcmNlbnQoMC4zOCkpIHwNCiAgICBlZmZpY2llbnphIDwgcGVyY2VudCgwLjQxKSB+ICJsaWdodGNvcmFsIiwNCiAgICANCiAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICApKQ0KDQoNCmZpZyA8LSBwbG90X2x5KA0KICB4ID0gZGF0YV9wbG90JHBsYXllcl9uYW1lLA0KICB5ID0gcm91bmQoZGF0YV9wbG90JGVmZmljaWVuemEsIGRpZ2l0cyA9IDIpLA0KICBtYXJrZXIgPSBsaXN0KGNvbG9yID0gZGF0YV9wbG90JGNvbG9yKSwNCiAgbmFtZSA9ICJFZmZpY2llbnphIGluIEF0dGFjY28iLA0KICB0eXBlID0gImJhciIsDQogIHRleHQgPSByb3VuZChkYXRhX3Bsb3QkZWZmaWNpZW56YSwgZGlnaXRzID0gMiksIHRleHRwb3NpdGlvbiA9ICdhdXRvJywgbWFya2VyID0gbGlzdChjb2xvciA9ICdyZ2IoMTU4LDIwMiwyMjUpJywgbGluZSA9IGxpc3QoY29sb3IgPSAncmdiKDgsNDgsMTA3KScsIHdpZHRoID0gMS41KSkpDQoNCmZpZyA8LSBmaWcgJT4lIGxheW91dCh0aXRsZSA9ICJFZmZpY2llbnphIGluIEF0dGFjY28iLA0KDQogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiIiksDQoNCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICIiKSkNCg0KDQpmaWcNCmBgYA0KDQpgYGB7cn0NCnNhdmVXaWRnZXQoZmlnLCAiQXR0YWNjby5odG1sIikNCmBgYA0KDQojIyBWb2xsZXliYWxsIERpc3RyaWJ1dGlvbg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoaHRtbHdpZGdldHMpDQoNCmZvciAoaSBpbiAxOjYpIHsNCiAgYXR0YWNrX3JhdGUgPC0gcHggJT4lIA0KICAgIGRwbHlyOjpmaWx0ZXIoc2tpbGwgPT0gIkF0dGFjayIsIHRlYW0gPT0gdGVhbU5hbWUsIHZpc2l0aW5nX3NldHRlcl9wb3NpdGlvbiA9PSBpKSAlPiUNCiAgICBncm91cF9ieShzdGFydF96b25lKSAlPiUgDQogICAgZHBseXI6OnN1bW1hcml6ZShuX2F0dGFja3MgPSBuKCkpICU+JQ0KICAgIG11dGF0ZShyYXRlID0gbl9hdHRhY2tzL3N1bShuX2F0dGFja3MpKSAlPiUgDQogICAgdW5ncm91cA0KDQogIGF0dGFja19yYXRlIDwtIGNiaW5kKGF0dGFja19yYXRlLCBkdl94eShhdHRhY2tfcmF0ZSRzdGFydF96b25lLCBlbmQgPSAibG93ZXIiKSkNCg0KICB0bTJpIDwtIGF0dGFja19yYXRlJHRlYW0gPT0gdGVhbXMocHgpWzJdDQogIGF0dGFja19yYXRlW3RtMmksIGMoIngiLCAieSIpXSA8LSBkdl9mbGlwX3h5KGF0dGFja19yYXRlW3RtMmksIGMoIngiLCAieSIpXSkNCg0KICBhdHRhY2tfcmF0ZSRyYXRlX3JvdW5kZWQgPC0gcm91bmQoYXR0YWNrX3JhdGUkcmF0ZSwgMikNCg0KICBmaWcyIDwtIGdncGxvdChhdHRhY2tfcmF0ZSwgYWVzKHgsIHksIGZpbGwgPSByYXRlX3JvdW5kZWQpKSArIA0KICAgIGdlb21fdGlsZSgpICsgDQogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChyb3VuZChyYXRlX3JvdW5kZWQgKiAxMDAsIDIpLCAiJSIpKSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMykgKw0KICAgIGdnY291cnQobGFiZWxzID0gdGVhbXMocHgpKSArDQogICAgc2NhbGVfZmlsbF9ncmFkaWVudDIobmFtZSA9ICJBdHRhY2sgcmF0ZSAoJSkiLCBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KGFjY3VyYWN5ID0gMC4wMSkpDQoNCiAgaHRtbF9uYW1lIDwtIHBhc3RlMCgiRGlzdHJpYnV6aW9uZSIsIGksICIucG5nIikNCiAgDQogICMgU2F2ZSB0aGUgcGxvdCBhcyBhbiBIVE1MIGZpbGUNCiAgZ2dzYXZlKGh0bWxfbmFtZSwgcGxvdCA9IGZpZzIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgdW5pdHMgPSAiaW4iLCBkcGkgPSAzMDApDQp9DQoNCmBgYA0K